{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "tags": [
     "parameters"
    ]
   },
   "outputs": [],
   "source": [
    "epochs = 10\n",
    "n_test_batches = 200"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 11 - সিকিউর (নিরাপদ) ডিপ লার্নিং ক্লাসিফিকেশন (শ্রেণীবিভাগ) (Secure Deep Learning Classification) \n",
    "\n",
    "\n",
    "\n",
    "## আপনার ডেটা গুরুত্বপূর্ণ, এবং আপনার মডেলও (Your data matters, your model too)\n",
    "\n",
    "মেশিন লার্নিং প্রধানত ডেটা দ্বারা চালিত। ফলে ডেটা তৈরি ও সংগ্রহকারী সংস্থাগুলো তাদের নিজস্ব মেশিন লার্নিং মডেল তৈরি ও ট্রেইন (Train) করতে সক্ষম। তারা অন্যান্য বাহ্যিক সংস্থাগুলিতে Model as a Service (MLaaS) পরিষেবা প্রদান করে থাকে। এটি খুবই উপকারি কেননা এর ফলে যেসকল সংস্থা নিজেদের স্বীয় মডেল তৈরি করতে অপারগ তারাও এই পরিষেবা দ্বারা নিজেদের ডেটার উপর ভবিষ্যদ্বাণী (Prediction) করতে পারে।\n",
    "\n",
    "তবে ক্লাউডে (cloud) হোস্ট (host) করা মডেলগুলোতে এখনও গোপনীয়তা/আইপি জনিত সমস্যা (privacy/IP issue) বিদ্যমান। MLaaS পরিষেবা ব্যবহার করার জন্য বাহ্যিক সংস্থাগুলিকে হয় তাদের নিজস্ব ইনপুট ডেটা ক্লাউডে আপলোড করতে হবে অথবা মডেলটি ডাউনলোড করতে হবে। গোপনীয়তার দৃষ্টিকোণ থেকে ইনপুট ডেটা আপলোড করা হতে পারে, তবে মডেলটি তৈরিকারি/মালিকানাধীন সংস্থা তাদের আইপি(IP) হারাতে উদ্বিগ্ন হলে তাদের মডেলটি ডাউনলোড করাও কোন বিকল্প হতে পারে না। \n",
    "\n",
    "## এনক্রিপ্ট করা ডেটা গণনা (Computing over encrypted data)\n",
    "\n",
    "এক্ষেত্রে একটি সম্ভাব্য সমাধান হল মডেল এবং ডেটা উভয়কে এমনভাবে এনক্রিপ্ট করা যাতে করে পরস্পরের আইপি প্রকাশ না করেই কোনও সংস্থা অন্য সংস্থার মালিকানাধীন একটি মডেল ব্যবহার করতে পারে। বেশ কয়েকটি এনক্রিপশন স্কিম আছে যা এনক্রিপ্ট হওয়া ডেটার উপর গণনা করতে পারে, যার মধ্যে সিকিউর মাল্টি-পার্টি কম্পিউটেশন (এসএমপিসি/SMPC), হোমোমর্ফিক এনক্রিপশন (FHE/SHE) এবং ফাংশনাল এনক্রিপশন (FE) সর্বাধিক পরিচিত। আমরা এখানে সিকিউর মাল্টি-পার্টি কম্পিউটেশন এর উপর দৃষ্টিপাত করব ([বিস্তারিত টিউটোরিয়াল ৫ এ](https://github.com/OpenMined/PySyft/blob/dev/examples/tutorials/Part%205%20-%20Intro%20to%20Encrypted%20Programs.ipynb)) যা প্রাইভেট অ্যাডিটিভ শেয়ারিং (Private additive sharing) দ্বারা গঠিত। এটি সিকিউরএনএন (SecureNN) এবং এসপিডিজেডের (SPDZ) মতো ক্রিপ্টো প্রোটোকলগুলির উপর নির্ভর করে, এর বিবরণ দেওয়া আছে [এই অসাধারণ ব্লগ পোস্ট এ](https://mortendahl.github.io/2017/09/19/private-image-analysis-with-mpc/). \n",
    "\n",
    "এই প্রোটোকলগুলি এনক্রিপ্ট করা ডেটার উপর দুর্দান্ত পারফর্ম করে এবং বিগত কয়েকমাস ধরে আমরা এই প্রোটোকলগুলির ব্যবহার সহজ করার জন্য কাজ করে যাচ্ছি। বিশেষত, আমরা প্রোটোকলগুলি পুনরায় শুরু থেকে ডেভেলপ না করে অথবা এদের পিছনে কার্যরত ক্রিপ্টোগ্রাফি না জেনেও যাতে প্রোটোকলগুলি ব্যবহার করা যায় সেজন্য টুলস (tools) নির্মাণ করছি। আসুন তবে শুরু করা যাক।\n",
    "\n",
    "## সেট-আপ (Set up)\n",
    "\n",
    "এই টিউটোরিয়ালে ব্যবহৃত  সেটআপ টি নিম্নরূপঃ ধরুন আপনি হচ্ছেন সার্ভার এবং আপনার কাছে কিছু ডেটা রয়েছে। প্রথমে, আপনার এই প্রাইভেট ট্রেনিং ডেটা দিয়ে আপনি একটি মডেল নির্ধারণ ও ট্রেইন করবেন। তারপর, আপনি একজন ক্লায়েন্ট পেলেন যিনি তার কিছু নিজস্ব ডেটা ব্যবহার করে আপনার মডেল এর সাহায্যে কিছু প্রেডিকশান করতে চান।\n",
    "\n",
    "আপনি আপনার মডেলটি (যা একটি নিউরাল নেটওয়ার্ক) এনক্রিপ্ট করলেন। অপরদিকে, আপনার ক্লায়েন্ট তাঁর ডেটা এনক্রিপ্ট করলেন। তারপর আপনারা উভয়ে এই এনক্রিপ্টেড মডেল ও ডেটা ব্যবহার করে ক্লায়েন্টের ডেটার শ্রেণীকরণ (Classification) করলেন। সবশেষে, শ্রেণীকরণের ফলাফলটিও ক্লায়েন্টের কাছে এনক্রিপ্টেড রূপে পাঠানো হয় যাতে করে সার্ভার (আপনি) আপনার ক্লায়েন্টের ডেটা (অথবা মডেলটির ইনপুট বা ফলাফল (আউটপুট)) সম্পর্কে কিছুই জানতে না পারেন।\n",
    "\n",
    "আদর্শগতভাবে, আমরা ক্লায়েন্টের ইনপুট ও সার্ভার এবং অপরদিকে মডেল ও ক্লায়েন্টের ইনপুট পরস্পরের মধ্যে মুষ্টিগতভাবে শেয়ার করে থাকি।  সরলতার খাতিরে, এই শেয়ারটি করা হয় alice ও bob নামের দুটি ওয়ার্কার (worker) এর মাধ্যমে। এক্ষেত্রে আপনি ধরতে পারেন যে alice কাজ করে ক্লায়েন্টের জন্য এবং bob কাজ করে সার্ভারের জন্য।\n",
    "\n",
    "সম্পূর্ণ প্রক্রিয়াটি একটি সৎ-তবে-কৌতূহলি (honest-but-curious adversary) মডেল এর ভেতর খুবই নিরাপদ যা [MPC framework](https://arxiv.org/pdf/1801.03239.pdf) গুলোতে বহুল ব্যবহৃত।\n",
    "\n",
    "**এ পর্যন্ত যা যা জানার দরকার ছিল তা আমরা জেনেছি, এখন আসুন শুরু করা যাক!**\n",
    "\n",
    "\n",
    "লেখক:\n",
    "- Théo Ryffel - Twitter: [@theoryffel](https://twitter.com/theoryffel) · GitHub: [@LaRiffle](https://github.com/LaRiffle)\n",
    "\n",
    "**চলুন শুরু করা যাক!**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "অনুবাদক:\n",
    "\n",
    "- Sourav Das - Twitter: [@adventuroussrv](https://twitter.com/adventuroussrv)\n",
    "- Zarreen Reza - Twitter: [@zarreennreza](https://twitter.com/zarreennreza)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### মডেল ইমপোর্ট ও বিবরন নির্ধারণ (Imports and model specifications)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "import torch.optim as optim\n",
    "from torchvision import datasets, transforms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "আমাদের PySyft এর জন্য নির্ধারিত কমান্ডগুলোও চালাতে হবে।। আমরা কিছু ওয়ার্কারও তৈরি করব (client, bob, এবং alice নামক)। অবশেষে, আমরা crypto_provider ডিফাইন করব যা আমাদের সকল প্রয়োজনীয় মৌলিক ক্রিপ্টো সুবিধাগুলো দিবে।([বিস্তারিত জানতে আমাদের SMPC টিউটোরিয়ালটি পড়ুন](https://github.com/OpenMined/PySyft/blob/master/examples/tutorials/Part%2009%20-%20Intro%20to%20Encrypted%20Programs.ipynb)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import syft as sy\n",
    "hook = sy.TorchHook(torch) \n",
    "client = sy.VirtualWorker(hook, id=\"client\")\n",
    "bob = sy.VirtualWorker(hook, id=\"bob\")\n",
    "alice = sy.VirtualWorker(hook, id=\"alice\")\n",
    "crypto_provider = sy.VirtualWorker(hook, id=\"crypto_provider\") "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "এখন আমরা মডেল লার্নিং এর জন্য সেটআপ নির্ধারণ করব।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Arguments():\n",
    "    def __init__(self):\n",
    "        self.batch_size = 64\n",
    "        self.test_batch_size = 50\n",
    "        self.epochs = epochs\n",
    "        self.lr = 0.001\n",
    "        self.log_interval = 100\n",
    "\n",
    "args = Arguments()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ডেটা লোড করা এবং ওয়ার্কারদের কাছে প্রেরণ (Data loading and sending to workers\n",
    "\n",
    "আমাদের নির্ধারিত সেটিং এ আমরা ধরে নিয়েছি যে সার্ভার এর কাছে প্রথমবার মডেল ট্রেইন করার জন্য কিছু ডেটা রয়েছে। এখানে আমরা MNIST ট্রেইনিং সেট ব্যবহার করছি।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_loader = torch.utils.data.DataLoader(\n",
    "    datasets.MNIST('../data', train=True, download=True,\n",
    "                   transform=transforms.Compose([\n",
    "                       transforms.ToTensor(),\n",
    "                       transforms.Normalize((0.1307,), (0.3081,))\n",
    "                   ])),\n",
    "    batch_size=args.batch_size, shuffle=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "দ্বিতীয়ত, ক্লায়েন্টের কাছে কিছু ডেটা আছে যার উপর তিনি সার্ভারের মডেল ব্যবহার করে শ্রেণীকরণ করতে ইচ্ছুক। তিনি দুই ওয়ার্কার alice ও bob এর সাথে তাঁর ডেটা শেয়ার করার মাধ্যমে ডেটা এনক্রিপ্ট করবেন।\n",
    "> SMPC তে ব্যবহৃত ক্রিপ্টো প্রটোকলগুলো পূর্ণসংখ্যার উপর কাজ করে। আমরা এখানে PySyft tensor abstraction এর সুবিধা নিয়ে  .fix_precision() মেথড ব্যবহার করে  PyTorch Float tensor কে Fixed Precision Tensor এ রূপান্তরিত করেছি। উদাহরণস্বরূপ, ০.১২৩ কে ২ এর প্রেসিশান (precision 2) দিয়ে পূর্ণসংখ্যায় রূপান্তর করলে ২য় দশমিক সংখ্যা পর্যন্ত নিয়ে সংখ্যাটি দাঁড়াবে ১২।\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test_loader = torch.utils.data.DataLoader(\n",
    "    datasets.MNIST('../data', train=False,\n",
    "                   transform=transforms.Compose([\n",
    "                       transforms.ToTensor(),\n",
    "                       transforms.Normalize((0.1307,), (0.3081,))\n",
    "                   ])),\n",
    "    batch_size=args.test_batch_size, shuffle=True)\n",
    "\n",
    "private_test_loader = []\n",
    "for data, target in test_loader:\n",
    "    private_test_loader.append((\n",
    "        data.fix_precision().share(alice, bob, crypto_provider=crypto_provider),\n",
    "        target.fix_precision().share(alice, bob, crypto_provider=crypto_provider)\n",
    "    ))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ফিড ফরোয়ার্ড নিউরাল নেটওয়ার্ক স্পেসিফিকেশন (Feed Forward Neural Network specification)\n",
    "সার্ভার দ্বারা ব্যবহৃত নেটওয়ার্ক এর স্পেসিফিকেশন এভাবে নির্ধারণ করব।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Net(nn.Module):\n",
    "    def __init__(self):\n",
    "        super(Net, self).__init__()\n",
    "        self.fc1 = nn.Linear(784, 500)\n",
    "        self.fc2 = nn.Linear(500, 10)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = x.view(-1, 784)\n",
    "        x = self.fc1(x)\n",
    "        x = F.relu(x)\n",
    "        x = self.fc2(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ট্রেইনিং চালুকরণ (Launch the training)\n",
    "যেহেতু ট্রেইনিং সম্পূর্ণ লোকালভাবে করা হয় তাই এটি পুরোটাই লোকাল PyTorch ট্রেইনিং ছাড়া আর বিশেষ কিছুই নয়!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def train(args, model, train_loader, optimizer, epoch):\n",
    "    model.train()\n",
    "    for batch_idx, (data, target) in enumerate(train_loader):\n",
    "        optimizer.zero_grad()\n",
    "        output = model(data)\n",
    "        output = F.log_softmax(output, dim=1)\n",
    "        loss = F.nll_loss(output, target)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        if batch_idx % args.log_interval == 0:\n",
    "            print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format(\n",
    "                epoch, batch_idx * args.batch_size, len(train_loader) * args.batch_size,\n",
    "                100. * batch_idx / len(train_loader), loss.item()))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = Net()\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=args.lr)\n",
    "\n",
    "for epoch in range(1, args.epochs + 1):\n",
    "    train(args, model, train_loader, optimizer, epoch)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test(args, model, test_loader):\n",
    "    model.eval()\n",
    "    test_loss = 0\n",
    "    correct = 0\n",
    "    with torch.no_grad():\n",
    "        for data, target in test_loader:\n",
    "            output = model(data)\n",
    "            output = F.log_softmax(output, dim=1)\n",
    "            test_loss += F.nll_loss(output, target, reduction='sum').item() # sum up batch loss\n",
    "            pred = output.argmax(1, keepdim=True) # get the index of the max log-probability \n",
    "            correct += pred.eq(target.view_as(pred)).sum().item()\n",
    "\n",
    "    test_loss /= len(test_loader.dataset)\n",
    "\n",
    "    print('\\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format(\n",
    "        test_loss, correct, len(test_loader.dataset),\n",
    "        100. * correct / len(test_loader.dataset)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test(args, model, test_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "আমাদের মডেল ট্রেইনিং সম্পন্ন হয়েছে এবং এটি একটি পরিষেবা আকারে সরবরাহ করার জন্য প্রস্তুত!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## নিরাপদ উপায়ে মডেল মূল্যায়ন (Secure evaluation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "এখন সার্ভার হিসাবে আমরা মডেলটি ক্লায়েন্টের ডেটা ধারণকারী ওয়ার্কারদের কাছে প্রেরন করব।  যেহেতু মডেলটিতে স্পর্শকাতর তথ্য রয়েছে (যা আমরা এ পর্যন্ত রক্ষা করে এসেছি!), আমরা এর weights বা প্যারামিটারগুলোও প্রকাশ করতে চাইনা। তাই আমরা ডেটাসেট এর মতোই গোপনীয়তার সাথে মডেলটি শেয়ার করব।"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.fix_precision().share(alice, bob, crypto_provider=crypto_provider)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "নিম্নের টেস্ট নামক ফাংশানটি এনক্রিপ্টেড উপায়ে মডেলের মূল্যায়ন করে। মডেলের weights, ডেটা ইনপুট, স্কোরিং এর জন্য ব্যবহৃত প্রেডিকশান ও টার্গেট সবকিছুই এনক্রিপ্টেড!\n",
    "\n",
    "তবে, এই কোডের কম্যান্ডগুলো ঠিক PyTorch দিয়ে মডেল টেস্ট করার কম্যান্ডের মতন, কি সুন্দর তাইনা!\n",
    "\n",
    "সবশেষে, আমরা শুধুমাত্র ফাইনাল স্কোরটা সার্ভার থেকে ডিক্রিপ্ট করি যা দিয়ে মডেলটি গড়ে একটি ভাল আউটপুট দিচ্ছে কিনা তা নিশ্চিত করা যায়। "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def test(args, model, test_loader):\n",
    "    model.eval()\n",
    "    n_correct_priv = 0\n",
    "    n_total = 0\n",
    "    with torch.no_grad():\n",
    "        for data, target in test_loader[:n_test_batches]:\n",
    "            output = model(data)\n",
    "            pred = output.argmax(dim=1) \n",
    "            n_correct_priv += pred.eq(target.view_as(pred)).sum()\n",
    "            n_total += args.test_batch_size\n",
    "# This 'test' function performs the encrypted evaluation. The model weights, the data inputs, the prediction and the target used for scoring are all encrypted!\n",
    "\n",
    "# However as you can observe, the syntax is very similar to normal PyTorch testing! Nice!\n",
    "\n",
    "# The only thing we decrypt from the server side is the final score at the end of our 200 items batches to verify predictions were on average good.      \n",
    "            n_correct = n_correct_priv.copy().get().float_precision().long().item()\n",
    "    \n",
    "            print('Test set: Accuracy: {}/{} ({:.0f}%)'.format(\n",
    "                n_correct, n_total,\n",
    "                100. * n_correct / n_total))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "test(args, model, private_test_loader)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "সাবাস! আপনি শিখে গেলেন কিভাবে শুরু থেকে শেষ পর্যন্ত এনক্রিপ্টেড উপায়ে প্রেডিকশান করতে হয়ঃ সার্ভারের মডেলের প্যারামিটারগুলো যেমন ক্লায়েন্টের কাছে ফাঁস হয়নি, তেমনি সার্ভারের কাছেও ডেটা ইনপুট ও শ্রেণীকরণ আউটপুট সম্পর্কে কোনও তথ্য নেই!\n",
    "\n",
    "পারফরম্যান্স সম্পরকে বলতে গেলে, একটি ইমেজ শ্রেণীকরণ করতে ০.১ সেকেন্ডেরও কম সময় লেগেছে, আমার ল্যাপটপে (২.৭ গিগা হার্টয ইন্টেল কোর আই৭, ১৬ জিবি র‍্যাম)  আনুমানিক ০.৩৩ মিঃসিঃ। তবে, এখানে ওয়ার্কারদের মধ্যে  খুবই দ্রুত যোগাযোগ সম্ভব হয়েছে  (সবগুলো ওয়ার্কার আমার লোকাল কম্পিউটারে থাকাতে)। ওয়ার্কারগুলো কত দ্রুত পরস্পরের সাথে যোগাযোগ করতে পারছে তার উপর নির্ভর করে পারফরম্যান্সের তারতম্য হতে পারে। "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## উপসংহার (Conclusion)\n",
    "\n",
    "আপনি দেখলেন যে একজন ক্রিপ্টো পারদর্শী না হয়েও PyTorch ও PySyft ব্যবহার করে কত সহজে ব্যবহারকারির তথ্য গোপন রেখে সেই তথ্যর উপর মেশিন লার্নিং প্রয়োগ করা যায়!\n",
    "\n",
    "এই বিষয়ে সামনে আরো লেখা আসবে, যেখানে কনভলিউশনাল লেয়ারের কথা থাকবে যাতে করে প্রচলিত লাইব্রেরীগুলোর সাথে তুলনা পূর্বক PySyft এর  পারফরম্যান্সের মানদণ্ড নির্ধারণ করা যাবে। এছাড়া সামনের লেখাগুলোয় নিউরাল নেটওয়ার্কের প্রাইভেট এনক্রিপ্টেড ট্রেইনিং নিয়েও বলা হবে, যা দিয়ে সংস্থাগুলো বাহ্যিক স্পর্শকাতর ডেটা ব্যবহার করে নিজেদের মডেল ট্রেইন করতে পারবে। সাথেই থাকুন!\n",
    "\n",
    "আপনি যদি এটি উপভোগ করে থাকেন এবং তথ্যর গোপনীয়তা রক্ষা পূর্বক, বিকেন্দ্রীভূত মালিকানাধিন এআই (AI) ও এআই (AI) সাপ্লাই চেইন (ডেটা) এর এই আন্দোলনকে সমর্থন করেন, নিম্নোক্ত উপায়ে আমাদের পাশে থাকতে পারেন!\n",
    "\n",
    "### গিটহাবে PySyft কে স্টার দিন (Star PySyft on GitHub)\n",
    "\n",
    "আমাদের সম্প্রদায়কে সাহায্য করার সবচেয়ে সহজ উপায় হল গিটহাব রিপোসিটোরি গুলোতে ষ্টার দেয়া।\n",
    " এটি আমরা যে অসাধারণ সরঞ্জামগুলি তৈরি করছি তার সচেতনতা বাড়াতে সহায়তা করে।\n",
    "\n",
    "- [Star PySyft](https://github.com/OpenMined/PySyft)\n",
    "\n",
    "### গিটহাবে আমাদের টিউটোরিয়ালগুলি চয়ন করুন (Pick our tutorials on GitHub!)\n",
    "\n",
    "ফেডারেটেড এবং প্রাইভেসি-প্রিজারভেভিং লার্নিংয়ের চেহারা কেমন হওয়া উচিত এবং আমরা এটির ভিত্তি ও কাঠামো কীভাবে তৈরি করছি সে সম্পর্কে আরও ভাল ধারণা পেতে আমরা কিছু দুর্দান্ত টিউটোরিয়াল তৈরি করেছি।\n",
    "\n",
    "- [Checkout the PySyft tutorials](https://github.com/OpenMined/PySyft/tree/master/examples/tutorials)\n",
    "\n",
    "\n",
    "### আমাদের স্ল্যাকে যোগ দিন (Join our Slack!)\n",
    "\n",
    "সর্বশেষ আপডেট পাবার সর্বোত্তম উপায় হল আমাদের সম্প্রদায়ে যোগদান করা!\n",
    "\n",
    "- [Join slack.openmined.org](http://slack.openmined.org)\n",
    "\n",
    "### একটি কোড প্রকল্পে যোগদান করুন (Join a Code Project!)\n",
    "\n",
    "আমাদের সম্প্রদায়ে অবদান রাখার সর্বোত্তম উপায় হল কোড প্রকল্পে অবদান রাখা! আপনি যদি এক-কালিন কোন মিনি-প্রকল্প শুরু করতে চান, তবে আপনি PySyft GitHub Issues পেইজে গিয়ে “Good First Issue” চিহ্নিত ইস্যুগুলো খুঁজতে পারেন।\n",
    "\n",
    "- [Good First Issue Tickets](https://github.com/OpenMined/PySyft/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)\n",
    "\n",
    "### দান করা (Donate)\n",
    "\n",
    "আপনি যদি আমাদের কোডের প্রকল্পে অবদান রাখতে না পারেন কিন্তু তবুও আমাদের সমর্থন করতে চান, তবে আমাদের Open Collective এর Backer হতে পারেন। সকল অনুদান আমাদের ওয়েব হোসটিং এবং বিভিন্ন সাম্প্রদায়িক কার্যক্রম যেমন হ্যাকাথন, মিট-আপ ইত্যাদি কাজে ব্যয় হয়!\n",
    "\n",
    "- [Donate through OpenMined's Open Collective Page](https://opencollective.com/openmined)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "celltoolbar": "Tags",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  },
  "nbTranslate": {
   "displayLangs": [
    "*"
   ],
   "hotkey": "alt-t",
   "langInMainMenu": true,
   "sourceLang": "en",
   "targetLang": "fr",
   "useGoogleTranslate": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
